home *** CD-ROM | disk | FTP | other *** search
- /* Replacement for StuartLog in Stuart2.4.
- *
- * This program is in the public domain. Use and abuse it as you see fit.
- *
- * This program requires setuid-root to run correctly. There's
- * now a Makefile that sets all of that up - so use make to compile
- * this. There will be warnings under NeXSTEP3.0. Of course,
- * consult Stuart's online documentation under Installation/slot
- * for more information.
- *
- *
- * The StuartLog tool in Stuart2.3 was a good idea, but it didn't
- * quite work. I found that it would often simply hang during the
- * login process, thus either hanging Stuart or not getting the
- * logging functions done. slog takes a new approach. It runs
- * continuously from the time Stuart is launched, and the same process
- * handles all logging functions. Stuart communicates with slog
- * via a private Speaker/Listener pair. slog also monitors the
- * parent Stuart process and exits on abnormal termination.
- *
- * Admire the code to connect to the parent process. I think it
- * cost me a kidney.
- *
- * scott hess
- * shess@ssesco.com
- */
- #import "SLogListener.h"
- #import <objc/HashTable.h>
- #import <libc.h>
- #import <grp.h>
- #import <lastlog.h>
- #import <utmp.h>
- #import <ttyent.h>
- #import <pwd.h>
- #import <mach.h>
- #import <mach_error.h>
- #import <dpsclient/dpsclient.h>
- #import <sys/notify.h>
-
- @interface SLogger : Object
- {
- SLogListener *listener;
- HashTable *slots;
- int uid;
- const char *name;
- port_t parentNotify;
- }
- - run;
- @end
- /* Locking open and close. Though flock() is not a good general-purpose
- * file locker due to NFS limitations. It works well for this case
- * since only the local machine can access the devices.
- */
- int lopen( const char *filename, int openFlags)
- {
- int fd=open( filename, openFlags);
- if( fd>-1) {
- flock( fd, LOCK_EX);
- }
- return fd;
- }
- int lclose( int fd)
- {
- flock( fd, LOCK_UN);
- return close( fd);
- }
- /* Fix ownerships and permissions on the named pty line. */
- void fixOwnership( const char *pty, int uid, int gid, int mod)
- {
- char dev[ 64];
- sprintf( dev, "/dev/%s", pty);
- chown( dev, uid, gid);
- chmod( dev, mod);
- }
- /* Write an entry to wtmp. */
- void writeWtmp( struct utmp *ut)
- {
- int f=lopen( "/usr/adm/wtmp", O_WRONLY | O_APPEND);
- if( f>=0) {
- write( f, ut, sizeof( struct utmp));
- lclose( f);
- } else {
- perror( "opening /usr/adm/wtmp");
- }
- }
- /* Write an entry to utmp. */
- void writeUtmp( struct utmp *ut, int slot)
- {
- if( slot>-1) {
- int f=lopen( "/etc/utmp", O_WRONLY);
- if( f>=0) {
- lseek( f, slot*sizeof( struct utmp), L_SET);
- write( f, ut, sizeof( struct utmp));
- lclose( f);
- } else {
- perror( "opening /etc/utmp");
- }
- }
- }
-
- @implementation SLogger
- /* Initialize uid to an invalid user id (0 is valid). */
- - init
- {
- self=[super init];
- if( self) {
- uid=-1;
- }
- return self;
- }
- /* Find the slot in the /etc/ttys file for the given device. Cache
- * a mapping from the device name to the slot number for future use.
- */
- -(int)getSlot:(const char *)device
- {
- if( ![slots isKey:device]) {
- struct ttyent *t;
- int slot;
-
- setttyent();
- for( slot=1; t=getttyent(); slot++) {
- if( !strcmp( device, t->ty_name)) {
- break;
- }
- }
- endttyent();
- if( !t) {
- slot=-1;
- }
- if( !slots) {
- slots=[HashTable allocFromZone:[self zone]];
- slots=[slots initKeyDesc:"*" valueDesc:"i" capacity:0];
- }
- device=NXUniqueString( device);
- [slots insertKey:device value:(void *)slot];
- return slot;
- } else {
- return (int)[slots valueForKey:device];
- }
- }
- /* Login a use on the given pty. */
- -(int)login:(char *)pty ownerships:(int)ownership
- utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
- {
- /* Cache a passwd entry for the user if needed. */
- if( uid==-1) {
- /* Grab a passwd entry, set up uid. This code was suggested
- * by der Mouse <mouse@larry.mcrcim.mcgill.edu>
- */
- char *user=getenv( "USER");
- struct passwd *pw=NULL;
- uid=getuid();
- if( user) {
- pw=getpwnam( user);
- }
- if( !pw || (uid && (uid!=pw->pw_uid))) {
- pw=getpwuid( uid);
- }
- if( pw) {
- uid=pw->pw_uid;
- }
- if( pw) {
- name=NXUniqueString( pw->pw_name);
- } else {
- name="Unknown";
- }
- }
- if( utmp || wtmp || lastlog) {
- struct utmp ut;
-
- /* Clean up the utmp entry. */
- bzero( &ut, sizeof( ut));
-
- /* Set up the ut_name field if necessary. */
- if( wtmp || utmp) {
- strncpy( ut.ut_name, name, sizeof( ut.ut_name));
- }
-
- /* Setup the line and time. */
- strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
- time( &( ut.ut_time));
-
- /* Log to lastlog as needed. */
- if( lastlog) {
- int f=lopen( "/usr/adm/lastlog", O_WRONLY);
- if( f>=0) {
- struct lastlog llog;
- bzero( &llog, sizeof( llog));
- llog.ll_time=ut.ut_time;
- strncpy( llog.ll_line, ut.ut_line, sizeof( llog.ll_line));
- lseek( f, uid*sizeof( llog), L_SET);
- write( f, &llog, sizeof( llog));
- lclose( f);
- } else {
- perror( "opening /usr/adm/lastlog");
- }
- }
-
- /* Log to utmp and wtmp as needed. */
- if( utmp) {
- writeUtmp( &ut, [self getSlot:pty]);
- }
- if( wtmp) {
- writeWtmp( &ut);
- }
- }
-
- /* If needed, set pty ownership to the new user, with permissions
- * set for owner read/write, group write. Group ownership set
- * to the tty group, if available.
- */
- if( ownership) {
- struct group *gr=getgrnam( "tty");
- fixOwnership( pty, uid, gr ? gr->gr_gid : -1, 0620);
- }
- return 0;
- }
- -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp
- {
- #if 0
- return [self login:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
- #else /* This may make more sense. */
- return [self login:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
- #endif
- }
- /* This version has the return parameter okFlag which will force
- * Stuart to wait for slog to finish the operation before continuing
- * execution.
- */
- -(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp ok:(int *)okFlag
- {
- return [self login:pty ownerships:ownership utmp:utmp];
- }
- -(int)logout:(char *)pty ownerships:(int)ownership
- utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
- {
- if( utmp || wtmp) {
- struct utmp ut;
-
- /* Clean up the utmp entry. */
- bzero( &ut, sizeof( ut));
-
- strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
- time( &( ut.ut_time));
- if( utmp) {
- writeUtmp( &ut, [self getSlot:pty]);
- }
- if( wtmp) {
- writeWtmp( &ut);
- }
- }
-
- /* If needed, set pty ownership back to root user, with permissions
- * set for all read/write. Group ownership reset to the tty
- * group, if available.
- */
- if( ownership) {
- struct group *gr=getgrnam( "tty");
- fixOwnership( pty, 0, gr ? gr->gr_gid : -1, 0666);
- }
- return 0;
- }
- -(int)logout:(char *)pty ownerships:(int)ownership utmp:(int)utmp
- {
- #if 0
- return [self logout:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
- #else /* This may make more sense. */
- return [self logout:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
- #endif
- }
- /* I use this routine to let Stuart _kindly_ ask slog to exit. I
- * don't want Stuart doing a kill() on slog while slog's in the middle
- * of something ...
- */
- -(void)exit
- {
- exit( 0);
- }
- /* Disconnect our controlling tty and connect to the console device. */
- - ttyDisconnect
- {
- int tty;
-
- tty=open( "/dev/tty", O_RDWR);
- if( tty>-1) {
- ioctl( tty, TIOCNOTTY, 0);
- close( tty);
- }
- tty=open( "/dev/console", O_WRONLY);
- setpgrp( 0, getpid());
- dup2( tty, 1);
- dup2( tty, 2);
- if( tty!=1 && tty!=2) {
- close( tty);
- }
- return self;
- }
- /* Catch inadvertant parent process death. */
- void notifyPortHandler( notification_t *msg, SLogger *self)
- {
- if( msg->notify_header.msg_id==NOTIFY_PORT_DELETED) {
- if( msg->notify_port==self->parentNotify) {
- [self exit];
- }
- }
- }
- - run
- {
- kern_return_t ret;
- msg_header_t initMsg;
- extern int getppid( void);
- task_t parentTask;
- port_t notify;
-
- [self ttyDisconnect];
-
- /* Set up objects for our Listening pleasure. */
- listener=[[SLogListener allocFromZone:[self zone]] init];
- [listener setDelegate:self];
- [listener usePrivatePort];
- [listener addPort];
- /* Give us plenty of leeway for when people logout.
- * Actually, even this isn't really that great,
- * but what can you do?
- */
- port_set_backlog( task_self(), [listener listenPort], PORT_BACKLOG_MAX);
-
- /* Find our parent's notify port. */
- ret=task_by_unix_pid( task_self(), getppid(), &parentTask);
- if( ret!=KERN_SUCCESS) {
- printf( "slog: Unable to get parent's task_t.\n");
- exit( 1);
- }
- ret=task_get_notify_port( parentTask, &parentNotify);
- initMsg.msg_remote_port=parentNotify;
- if( ret!=KERN_SUCCESS) {
- printf( "slog: Unable to get parent's notify port.\n");
- exit( 1);
- }
- port_allocate( task_self(), ¬ify);
- task_set_notify_port( task_self(), notify);
- DPSAddPort( notify, (void *)notifyPortHandler, 64, self, 31);
-
- /* Set up the rest of the header. */
- initMsg.msg_simple=TRUE;
- initMsg.msg_size=sizeof( initMsg);
- initMsg.msg_type=MSG_TYPE_NORMAL;
- initMsg.msg_id=0;
-
- /* Including the port which our Listener listens on. */
- initMsg.msg_local_port=[listener listenPort];
-
- /* Send it, and if successful, enter the event loop. */
- ret=msg_send( &initMsg, SEND_TIMEOUT, 30000);
- if( ret==KERN_SUCCESS) {
- [Listener run];
- }
- printf( "slog: Unable to send Listener port to parent.\n");
- exit( 1);
- return self;
- }
- @end
-
- void main( void)
- {
- SLogger *logger=[[SLogger alloc] init];
- [logger run];
- }
-